#!/bin/bash
# /**
# * Copyright (c) 2021-2025, God's harbor
# * All rights reserved.
# * @file    code_stati.sh
# * @brief   统计指定目录下的代码行数
# * @author  Jason Zhou, yzfcer@163.com
# * @version 1.0
# * @date    2021-11-25
# */

# Global variables for totals
total_files=0
total_lines=0
total_code=0
total_comments=0
total_blanks=0

# Function to display help information
show_help() {
    echo "Usage: $(basename "$0") [OPTION]..."
    echo "Code statistics tool - counts lines of code, comments, and blanks"
    echo
    printf "  %-20s %s\n" "-d, --dir DIRECTORY" "Specify directory to analyze"
    printf "  %-20s %s\n" "-f, --file FILE" "Specify single file to analyze"
    printf "  %-20s %s\n" "-t, --type TYPES" "File extensions to include (separated by |), e.g., \"c|h|js\""
    printf "  %-20s %s\n" "" "Required when using -d, ignored with -f"
    printf "  %-20s %s\n" "-h, --help" "Display this help and exit"
    echo
    echo "Examples:"
    printf "  %-25s %s\n" "$(basename "$0") -d src -t \"c|h\"" "# Analyze all .c and .h files in src directory"
    printf "  %-25s %s\n" "$(basename "$0") -f main.c" "# Analyze single main.c file"
}

# Optimized file analysis function
analyze_file() {
    local file=$1
    local total=0 code=0 comments=0 blanks=0
    local in_comment_block=false

    # echo "[File] $file"

    # Use mapfile for faster reading
    mapfile -t lines < "$file"
    
    for line in "${lines[@]}"; do
        ((total++))
        
        # Trim whitespace (bash builtin method)
        trimmed_line="${line#"${line%%[![:space:]]*}"}"
        trimmed_line="${trimmed_line%"${trimmed_line##*[![:space:]]}"}"
        
        # Blank line check
        if [[ -z "$trimmed_line" ]]; then
            ((blanks++))
            continue
        fi
        
        # Comment block detection
        if [[ "$line" =~ ^[[:space:]]*/\* ]]; then
            if [[ "$line" =~ \*/[[:space:]]*$ ]]; then
                ((comments++))
            else
                in_comment_block=true
                ((comments++))
            fi
            continue
        fi
        
        if $in_comment_block; then
            ((comments++))
            [[ "$line" =~ \*/[[:space:]]*$ ]] && in_comment_block=false
            continue
        fi
        
        # Single-line comments
        if [[ "$line" =~ ^[[:space:]]*// || "$line" =~ ^[[:space:]]*# ]]; then
            ((comments++))
            continue
        fi
        
        # Code line
        ((code++))
    done
    
    # Aligned output
    printf "%-8s %-8s %-8s %-8s %s\n" "$total" "$code" "$comments" "$blanks" "$file"
    
    # Update totals
    ((total_files++))
    ((total_lines+=total))
    ((total_code+=code))
    ((total_comments+=comments))
    ((total_blanks+=blanks))
}

# Summary function with aligned output
show_summary() {
    local divider="===================================================================="
    echo
    echo "SUMMARY"
    echo "$divider"
    printf "%-24s : %-10d\n" "Total files analyzed" "$total_files"
    printf "%-24s : %-10d\n" "Total lines" "$total_lines"
    printf "%-24s : %-10d\n" "  Code lines" "$total_code"
    printf "%-24s : %-10d\n" "  Comment lines" "$total_comments"
    printf "%-24s : %-10d\n" "  Blank lines" "$total_blanks"
    echo "$divider"
}

# Main processing function
main() {
    target_dir="./"
    file_types="c|h"
    # Parse arguments
    while [[ $# -gt 0 ]]; do
        case $1 in
            -d|--dir)
                target_dir="$2"
                shift 2
                ;;
            -f|--file)
                target_file="$2"
                shift 2
                ;;
            -t|--type)
                file_types="$2"
                shift 2
                ;;
            -h|--help)
                show_help
                exit 0
                ;;
            *)
                echo "Error: Invalid option '$1'" >&2
                show_help >&2
                exit 1
                ;;
        esac
    done

    # Validate arguments
    if [[ -n "$target_file" && -n "$target_dir" ]]; then
        echo "Error: Cannot specify both file and directory" >&2
        show_help >&2
        exit 1
    fi

    if [[ -n "$target_file" ]]; then
        [[ -f "$target_file" ]] || { echo "Error: File '$target_file' does not exist" >&2; exit 1; }
        printf "Total lines     Code lines      Comment lines   Blank lines      file\n"
        analyze_file "$target_file"
    elif [[ -n "$target_dir" ]]; then
        target_dir=$(realpath "$target_dir")
        [[ -d "$target_dir" ]] || { echo "Error: Directory '$target_dir' does not exist" >&2; exit 1; }
        [[ -n "$file_types" ]] || { echo "Error: File type (-t) must be specified" >&2; show_help >&2; exit 1; }
        
        # Build find command properly for multiple extensions
        IFS='|' read -ra extensions <<< "$file_types"
        find_args=(-type f)
        if [[ ${#extensions[@]} -gt 0 ]]; then
            find_args+=(-name "*.${extensions[0]}")
            for ((i=1; i<${#extensions[@]}; i++)); do
                find_args+=(-o -name "*.${extensions[i]}")
            done
        fi
        echo "find $target_dir ${find_args[@]} -print0"

        # Process files
        printf "Total    Code     Comment  Blank    file\n"
        for file in $(find $target_dir ${find_args[@]}); do
            analyze_file "$file"
        done
    else
        echo "Error: Either file or directory must be specified" >&2
        show_help >&2
        exit 1
    fi

    [[ $total_files -gt 0 ]] && show_summary
}

# Start execution
# [[ $# -eq 0 ]] && { show_help; exit 1; }
main "$@"
